/*
 ============================================================================
 Name        : Assembler.c
 Author      : Sherry Krikszer
 Version     : 1.0
 Copyright   : --
 Description :  in C, ANSI-style
 ============================================================================
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "Assembler.h"
#include "FileHandler.h"
#include "FirstPass.h"
#include "SecondPass.h"
#include "CommandParser.h"

/* ----------------------------------------------------------------------------
 *	Constants Definition
 * ----------------------------------------------------------------------------*/

#define SOURCE_FILE_SUFFIX		".as"
#define OBJECT_FILE_SUFFIX		".ob"
#define ENTRY_FILE_SUFFIX		".ent"
#define EXTERN_FILE_SUFFIX		".ext"

#define SYMBOL_END_MARKER		':'
#define COMMENT_LINE_MARKER  	';'
#define DIRECTIVE_START_MARKER	'.'
#define PARAMETER_SEPARATOR 	','

#define MAX_PROGRAM_NAME_LENGTH	20

#define BASE 12
#define SIZE_OF_WORD_IN_BITS 16
#define WORD_BITMASK ((1 << SIZE_OF_WORD_IN_BITS) - 1)

/* ----------------------------------------------------------------------------
 *	Constants Definition End
 * ----------------------------------------------------------------------------*/

/* ----------------------------------------------------------------------------
 *	Macro Definition
 * ----------------------------------------------------------------------------*/

#define IS_WHITE_SPACE(ch) 		((isspace(ch)) 					? TRUE : FALSE)
#define IS_NOT_EMPTY_LINE(ch) 	((ch != LINE_END) 				? TRUE : FALSE)
#define IS_NOT_COMMENT_LINE(ch) ((ch != COMMENT_LINE_MARKER)	? TRUE : FALSE)

/* ----------------------------------------------------------------------------
 *	Macro Definition End
 * ----------------------------------------------------------------------------*/

/* ----------------------------------------------------------------------------
 *	Type Definition
 * ----------------------------------------------------------------------------*/

/* ----------------------------------------------------------------------------
 *	Type Definition End
 * ----------------------------------------------------------------------------*/

/* ----------------------------------------------------------------------------
 *	Function Declaration
 * ----------------------------------------------------------------------------*/

void InitProgramMemory();
void InitSymbolTables();

void InitCurrentProgramLines();
void FreeProgramLines(ProgramLines* currProgramLines);

CommandLineTokens* CreateCommandLineTokens();
void FreeCommandLineTokens(CommandLineTokens* commandLineTokens);

FileLines* ReadProgramFile(const char *fileName);

void ParseCurrentProgramLines(FileLines* programFileLines, ProgramLines* currentProgramLines);

Boolean ShouldHandleLine(FileLine* fileLine);
StatementType ClasifyStatement(char* line);

Boolean IsValidSymbol 		(char* symbolStart, char* symbolEnd, char* invalidMessage);

Boolean ExtractSymbol		(char** fileLine, char* extractSymbolTo			, char* failMessage);
Boolean ExtractCommandName	(char** fileLine, char* extractCommandNameTo	, char* failMessage);
Boolean ExtractDirective	(char** fileLine, char* extractDirectiveTo		, char* failMessage);
Boolean ExtractParameters	(char** fileLine, Params* extractParamatersTo	, char* failMessage);

char* FindTokenEnd(char* string);
char* TrimWhiteSpaces(char* string);

void SwitchSourceAndDestParams(Params* params);
void ConvertToBase12(unsigned int valToConvert, char* base12Val);
void ConvertWordToBase12(Word* wordToCode, char* base12Word);

Boolean WriteOutPutFiles(Memory* instructions, SymbolTable* entrySymbols, SymbolTable* externSymbols);

void PrintErrorLines(ProgramLines* currProgramLines);
//void trimWhiteSpaces(const char *fileName);
//DivideTokensResult DivideCommandLineTokens(char* currLine, int lineNumber);

/* ----------------------------------------------------------------------------
 *	Function Declaration End
 * ----------------------------------------------------------------------------*/

/* ----------------------------------------------------------------------------
 *	Variable Definition
 * ----------------------------------------------------------------------------*/

char currProgramFileName[MAX_PROGRAM_NAME_LENGTH];
char currFullProgramFileName[MAX_PROGRAM_NAME_LENGTH];

Memory programMemory;

SymbolTable symbolTable, externSymbolTable;

ProgramLines currProgramLines;

char Int12ToEncodingChar[] =
{
	/*0*/ '0',
	/*1*/ '1',
	/*2*/ '2',
	/*3*/ '3',
	/*4*/ '4',
	/*5*/ '5',
	/*6*/ '6',
	/*7*/ '7',
	/*8*/ '8',
	/*9*/ '9',
	/*10*/ 'A',
	/*11*/ 'B',
};

/* ----------------------------------------------------------------------------
 *	Variable Definition End
 * ----------------------------------------------------------------------------*/

/* ----------------------------------------------------------------------------
 *	Function Definition
 * ----------------------------------------------------------------------------*/

void FreeProgramLines(ProgramLines* currProgramLines)
{
	int index;

	for (index = 0; index < currProgramLines->lineCounter; index++)
	{
		ParsedProgramLine currProgramLine = currProgramLines->lines[index];

		FreeCommandLineTokens(currProgramLine.commandTokens);
		FreeCommandParsedOperands(currProgramLine.commadParsedOperands);
		currProgramLine.errorMessage[0] = LINE_END;
		currProgramLine.originalFileLine = NULL;
		currProgramLine.parseErrorOccured = FALSE;
		currProgramLine.shouldBeHandled = TRUE;
	}
}

/* TODO : write documentation for this method */
void AssembleProgramFile(const char* programFileName)
{
	Boolean hasErrorOccured = TRUE;

	FileLines* programFileLines = NULL;

	/* Initialize the current program's memory and symbol tables */
	InitProgramMemory();
	InitSymbolTables();

	/* Save the given assembled file name and add the source suffix (".as")  */
	//int programFileNameLength = strlen(programFileName);
	strcpy(currProgramFileName, programFileName);
	strcpy(currFullProgramFileName, currProgramFileName);
	strncat(currFullProgramFileName, SOURCE_FILE_SUFFIX, strlen(SOURCE_FILE_SUFFIX));

	/* Attempt to open the file and load the it's lines */
	programFileLines = ReadProgramFile(currFullProgramFileName);

	/* In case file load succeeded */
	if (programFileLines != NULL && (programFileLines->lineCounter) > 0)
	{
		InitCurrentProgramLines();

		ParseCurrentProgramLines(programFileLines, &currProgramLines);

		// TODO : Remove this block when done
//		{
//			//test block
//
//			int i;
//
//			for (i = 0; i < currProgramLines.lineCounter; ++i)
//			{
//				printf("%s :\n", currProgramLines.lines[i].originalFileLine->line);
//
//				if (currProgramLines.lines[i].commandTokens != NULL)
//				{
//					printf("Symbol : %s, ", currProgramLines.lines[i].commandTokens->symbol);
//
//					if (currProgramLines.lines[i].commandTokens->statementType == DIRECTIVE)
//					{
//						printf("Directive : %s, ",
//								currProgramLines.lines[i].commandTokens->statement.directive);
//					}
//					else if (currProgramLines.lines[i].commandTokens->statementType == COMMAND)
//					{
//						printf("Command : %s, ",
//								currProgramLines.lines[i].commandTokens->statement.commandName);
//					}
//
//					int j;
//
//					printf("Operands : ");
//
//					if (currProgramLines.lines[i].commandTokens->params.paramCounter > 0)
//					{
//						for (j = 0; j < currProgramLines.lines[i].commandTokens->params.paramCounter; ++j)
//						{
//							printf("%s", currProgramLines.lines[i].commandTokens->params.paramValues[j]);
//							if (j + 1 < currProgramLines.lines[i].commandTokens->params.paramCounter)
//							{
//								printf(", ");
//							}
//						}
//					}
//					else
//					{
//						printf("none");
//					}
//				}
//				else
//				{
//					printf("Error : %s", currProgramLines.lines[i].errorMessage);
//				}
//
//				printf("\n");
//			}
//		}

		/* Call first pass */
		if (PerformFirstPass(&currProgramLines, &programMemory,
				&symbolTable, &externSymbolTable) == TRUE)
		{
			/* Call second pass */
			/* use these tables as output */
			SymbolTable entryRefrenceTable, externRefrenceTable;

			entryRefrenceTable.SC = 0;
			externRefrenceTable.SC = 0;

			if (PerformSecondPass(&currProgramLines, &programMemory,
					&symbolTable, &externSymbolTable, &entryRefrenceTable, &externRefrenceTable) == TRUE)
			{
				hasErrorOccured = FALSE;
				entryRefrenceTable.symbolBaseAdress = symbolTable.symbolBaseAdress;

				WriteOutPutFiles(&programMemory, &entryRefrenceTable, &externRefrenceTable);
			}
		}
	}

	if (hasErrorOccured)
	{
		PrintErrorLines(&currProgramLines);
	}

	FreeProgramLines(&currProgramLines);

	FreeFileLines(programFileLines);
}

void PrintErrorLines(ProgramLines* currProgramLines)
{
	int index;

	for (index = 0; index < currProgramLines->lineCounter; index++)
	{
		if (currProgramLines->lines[index].parseErrorOccured == TRUE)
		{
			fprintf(stderr, " Error found on command '%s': %s",
				currProgramLines->lines[index].originalFileLine, 
				currProgramLines->lines[index].errorMessage);
		}
	}
}

/* TODO : write documentation for this method */
void ParseCurrentProgramLines(FileLines* programFileLines, ProgramLines* currentProgramLines)
{
	int currLineIndex;

	/* Go over all the raw file lines */
	for (currLineIndex = 0; currLineIndex < programFileLines->lineCounter; currLineIndex++)
	{
		/* Get current file line */
		FileLine* currFileLine = &(programFileLines->FileLine[currLineIndex]);

		if (currFileLine != NULL)
		{
			/* Get the program line that will hold the parsed line */
			ParsedProgramLine* currHandledLine = &(currentProgramLines->lines[currLineIndex]);

			currHandledLine->commandTokens = NULL;

			/* Save the original file line */
			currHandledLine->originalFileLine = currFileLine;
			currHandledLine->originalFileLine->lineNumber = currLineIndex;
			if (ShouldHandleLine(currHandledLine->originalFileLine) == TRUE)
			{
				char* line = currHandledLine->originalFileLine->line;

				CommandLineTokens* currCommandTokens = CreateCommandLineTokens();

				currHandledLine->shouldBeHandled = TRUE;

				if (ExtractSymbol(&line, currCommandTokens->symbol,
						currHandledLine->errorMessage) == TRUE)
				{
					Boolean statementReadSucceeded = FALSE;

					currCommandTokens->statementType = ClasifyStatement(line);

					switch (currCommandTokens->statementType)
					{
						case DIRECTIVE:
							if (ExtractDirective(&line, currCommandTokens->statement.directive,
									currHandledLine->errorMessage) == TRUE)
							{
								statementReadSucceeded = TRUE;
							}
							else
							{
								currHandledLine->parseErrorOccured = TRUE;
							}
						break;

						case COMMAND:
							if (ExtractCommandName(&line, currCommandTokens->statement.commandName,
									currHandledLine->errorMessage))
							{
								statementReadSucceeded = TRUE;
							}
							else
							{
								currHandledLine->parseErrorOccured = TRUE;
							}
						break;
					}

					if (statementReadSucceeded == TRUE)
					{
						if (ExtractParameters(&line, &(currCommandTokens->params),
								currHandledLine->errorMessage) == TRUE)
						{
							currHandledLine->commandTokens = currCommandTokens;
						}
						else
						{
							printf("Error ExtractParameters");
						}
					}
					else
					{
						currHandledLine->shouldBeHandled = FALSE;
					}
				}
				else
				{
					currHandledLine->parseErrorOccured = TRUE;
				}
			}
			else
			{
				currHandledLine->shouldBeHandled = FALSE;
			}
		}

		currProgramLines.lineCounter++;
	}
}

Boolean ShouldHandleLine(FileLine* fileLine)
{
	Boolean shouldHandleLine = FALSE;

	if (fileLine != NULL)
	{
		char* currLine = fileLine->line;

		if (*currLine != COMMENT_LINE_MARKER)
		{
			currLine = TrimWhiteSpaces(currLine);

			if (*currLine != LINE_END)
			{
				shouldHandleLine = TRUE;
			}
		}
	}

	return shouldHandleLine;
}

/* TODO : write documentation for this method */
void InitProgramMemory()
{
	programMemory.DC = 0;
	programMemory.IC = 0;
}

/* TODO : write documentation for this method */
void InitSymbolTables()
{
	symbolTable.SC = 0;
	externSymbolTable.SC = 0;
}

/* TODO : write documentation for this method */
void InitCurrentProgramLines()
{
	currProgramLines.lineCounter = 0;
}

/* TODO : write documentation for this method */
CommandLineTokens* CreateCommandLineTokens()
{
	CommandLineTokens* newCommandLineTokens = (CommandLineTokens*) malloc (sizeof(CommandLineTokens));

	*(newCommandLineTokens->symbol) = LINE_END;
	newCommandLineTokens->statementType = DIRECTIVE ;
	*(newCommandLineTokens->statement.directive) = LINE_END;
	newCommandLineTokens->params.paramCounter = 0;

	return newCommandLineTokens;
}

/* TODO : write documentation for this method */
void FreeCommandLineTokens(CommandLineTokens* commandLineTokens)
{
	if (commandLineTokens != NULL)
	{
		free(commandLineTokens);
		commandLineTokens = NULL;
	}
}

/* TODO : write documentation for this method */
Boolean ExtractSymbol (char** fileLine, char* extractSymbolTo, char* failMessage)
{
	Boolean extractSymbolSucceeded = FALSE;

	/* Trim spaces if any exists at the beginning of the line */
	char* line = TrimWhiteSpaces(*fileLine);

	/* Set symbol start */
	char* symbolStart = line;

	/* Attempt to find token end */
	char* symbolEnd = FindTokenEnd(line);

	/* Set the symbol to empty */
	*extractSymbolTo = LINE_END;

	/* Check that the given token is a valid symbol, include an additional character for the ':'
	 * that in case of a symbol is mandatory */
	if (IsValidSymbol(symbolStart, symbolEnd, failMessage) == TRUE)
	{
		int symbolLength = symbolEnd - symbolStart;

		if (symbolLength <= MAX_SYMBOL_NAME_LENGTH)
		{
			if (symbolLength > 0)
			{
				strncpy(extractSymbolTo, symbolStart, symbolLength);
				extractSymbolTo[symbolLength] = '\0';
				/* Move the line pass the symbol and end symbol marker */
				line += symbolLength + 1;
			}

			extractSymbolSucceeded = TRUE;

			*fileLine = line;
		}
		else
		{
			sprintf(failMessage, "Invalid symbol name, symbol length cannot exceed %d characters",
					MAX_SYMBOL_NAME_LENGTH);
		}
	}
	else
	{
		extractSymbolSucceeded = TRUE;
	}

	return extractSymbolSucceeded;
}

Boolean IsValidSymbol (char* symbolStart, char* symbolEnd, char* invalidMessage)
{
	//char* currCharacter = symbolStart;
	Boolean isValidSymbol = FALSE;

	if ((*symbolEnd != LINE_END) && ((*symbolEnd) == SYMBOL_END_MARKER))
	{
		int symbolLength = symbolEnd - symbolStart;

		if (symbolLength > 0)
		{
			char symbolName[MAX_SYMBOL_NAME_LENGTH];
			strncpy(symbolName, symbolStart, symbolLength);
			/* need to add the null terminating char*/
			symbolName[symbolLength] = '\0';
			isValidSymbol = IsValidLabel(symbolName, invalidMessage);
		}
	}

//	if ((*symbolEnd != LINE_END) && ((*symbolEnd) == SYMBOL_END_MARKER))
//	{
//		if (isalpha(*currCharacter))
//		{
//			Boolean areAllNumbersOrLetters = TRUE;
//
//			while (++currCharacter != symbolEnd) /* Check up until symbol end marker */
//			{
//				if (!isalnum(*symbolStart))
//				{
//					areAllNumbersOrLetters = FALSE;
//				}
//			}
//
//			if (areAllNumbersOrLetters == TRUE)
//			{
//				isValidSymbol = TRUE;
//			}
//			else
//			{
//				invalidMessage = "Invalid symbol name, symbol name consists of letters and digits";
//			}
//		}
//		else
//		{
//			invalidMessage = "Invalid symbol name, symbol name must start with a letter";
//		}
//	}

	return isValidSymbol;
}

/* TODO : write documentation for this method */
Boolean ExtractCommandName (char** fileLine, char* extractCommandNameTo, char* failMessage)
{
	Boolean extractCommandNameSucceeded = FALSE;

	/* Trim spaces if any exists at the beginning of the line */
	char* line = TrimWhiteSpaces(*fileLine);

	/* Set symbol start */
	char* commandNameStart = line;

	/* Attempt to find token end */
	char* commandNameEnd = FindTokenEnd(line);

	int commandNameLength = commandNameEnd - commandNameStart;

	if (commandNameLength <= MAX_COMMAND_NAME_LENGTH)
	{
		if (commandNameLength > 0)
		{
			strncpy(extractCommandNameTo, commandNameStart, commandNameLength);

			extractCommandNameTo[commandNameLength] = '\0';

			/* Move the line pass the command name */
			line += commandNameLength;

			if (DoesContainCommand(extractCommandNameTo) == TRUE)
			{
				extractCommandNameSucceeded = TRUE;

				*fileLine = line;
			}
			else
			{
				sprintf(failMessage, "Invalid command name, unknown command '%s'",
						extractCommandNameTo);

				extractCommandNameTo = EMPTY_LINE;
			}
		}
		else
		{
			extractCommandNameSucceeded = TRUE;
		}
	}
	else
	{
		sprintf(failMessage, "Invalid command name, command name length cannot exceed %d characters",
				MAX_COMMAND_NAME_LENGTH);
	}

	return extractCommandNameSucceeded;
}

/* TODO : write documentation for this method */
Boolean ExtractDirective (char** fileLine, char* extractDirectiveTo, char* failMessage)
{
	Boolean extractDirectiveSucceeded = FALSE;

	/* Trim spaces if any exists at the beginning of the line */
	char* line = TrimWhiteSpaces(*fileLine);

	/* Set symbol start */
	char* directiveStart = line;

	/* Attempt to find token end */
	char* directiveEnd = FindTokenEnd(line);

	if (*directiveStart == DIRECTIVE_START_MARKER)
	{
		int directiveLength;

		/* Move the line pass the directive start marker */
		directiveStart++;

		directiveLength = directiveEnd - directiveStart;

		if (directiveLength <= MAX_DIRECTIVE_NAME_LENGTH)
		{
			if (directiveLength > 0)
			{
				strncpy(extractDirectiveTo, directiveStart, directiveLength);
				extractDirectiveTo[directiveLength] = '\0';
				/* Move the line pass the directive name */
				line += directiveLength + 1;

				extractDirectiveSucceeded = TRUE;

				*fileLine = line;
			}
		}
		else
		{
			sprintf(failMessage, "Invalid directive name, unknown directive '%s'",
					extractDirectiveTo);

			extractDirectiveTo = EMPTY_LINE;
		}
	}
	else
	{
		extractDirectiveSucceeded = TRUE;
	}

	return extractDirectiveSucceeded;
}

/* TODO : write documentation for this method */
Boolean ExtractParameters (char** fileLine, Params* extractParamatersTo, char* failMessage)
{
	Boolean extractParametersSucceeded = FALSE;

	/* Trim spaces if any exists at the beginning of the line */
	char* line = TrimWhiteSpaces(*fileLine);

	int parametersCounter = 0;

	Boolean allExtractParametersSucceeded = TRUE;

	/* As long as line end wasn't encountered */
	while (*line != LINE_END)
	{
		/* Set parameter start */
		char* parameterStart = line;

		/* Attempt to find token end */
		char* parameterEnd = FindTokenEnd(line);

		int parameterLength;

		/* In case that the parameters counter reached maximum count
		 * And file line still contains parameters */
		if (parametersCounter >= MAX_OPERANDS_COUNT)
		{
			/* The extract parameters should fail and notify the user
			 * that too many parameters were supplied*/
			allExtractParametersSucceeded = FALSE;

			failMessage = "Invalid parameters, too many parameters supplied";

			extractParamatersTo = NULL;

			break;
		}		

		/* Calculate parameter string length */
		parameterLength = parameterEnd - parameterStart;

		if (parameterLength <= MAX_OPERAND_STRING_LENGTH)
		{
			if (parameterLength > 0)
			{
				/* Get the parameter string from the file line */
				strncpy(extractParamatersTo->paramValues[parametersCounter],
						parameterStart, parameterLength);
				extractParamatersTo->paramValues[parametersCounter][parameterLength] = '\0';
				parametersCounter++;

				/* Move the line pass the current parameter */
				line += parameterLength;

				/* Trim spaces if any exists up to the end of the line or a preceding parameter */
				line = TrimWhiteSpaces(line);

				/* Only end line or parameter separator expected now */
				if (*line == PARAMETER_SEPARATOR)
				{
					/* Move pass the parameter separator (',') and prepare to read next parameter */
					line++;
				}
				else if (*line != LINE_END)
				{
					/* In case a character other then line end ('\0') was found the parameters line
					 * is in an invalid format and extract parameters should fail*/
					allExtractParametersSucceeded = FALSE;

					sprintf(failMessage,
							"Invalid parameters, '%c' was unexpected in %s separate parameters with '%c'",
							*line, line, PARAMETER_SEPARATOR);

					extractParamatersTo = NULL;

					break;
				}
			}
		}
		else
		{
			allExtractParametersSucceeded = FALSE;

			sprintf(failMessage, "Invalid parameter name, parameter number '%d' is to long",
					parametersCounter);

			extractParamatersTo = NULL;

			break;
		}

		line = TrimWhiteSpaces(line);
	}

	extractParamatersTo->paramCounter = parametersCounter;
	if (allExtractParametersSucceeded == TRUE)
	{
		extractParametersSucceeded = TRUE;

		SwitchSourceAndDestParams(extractParamatersTo);

		*fileLine = line;
	}

	return extractParametersSucceeded;
}

void SwitchSourceAndDestParams(Params* params)
{
	if (params->paramCounter == 2)
	{
		char tempParam[MAX_OPERAND_STRING_LENGTH];
		char* sourceParam = params->paramValues[0];
		char* destParam = params->paramValues[1];

		strcpy(tempParam, sourceParam);
		strcpy(sourceParam, destParam);
		strcpy(destParam, tempParam);
	}
}

/* Reads the program file who's name is given and returns the program's instructions as a list
 * if read fails a null pointer is returned
 * Note : the list is initialized within this method but the calling method is responsible
 * 		  to free the returned list */
FileLines* ReadProgramFile(const char *fileName)
{
	return ReadLinesFromFile(fileName);
}

Boolean WriteMemoryFile(char* objectFileName, Memory* instruction)
{
	Boolean writeMemoryFile = FALSE;

	int i = 0;
	FILE* objFile = fopen(objectFileName, "w");

	if (objFile == NULL)
	{
		writeMemoryFile = FALSE;
	}

	for (i = 0; i < instruction->IC; i++)
	{
		char AddressStr[5] = "0000";
		char InstructionString[9] = "00000000";
		ConvertToBase12(i + instruction->codeBaseAddress, AddressStr);
		ConvertWordToBase12(&(instruction->codeSegment[i].machineCode.wordEncoding), InstructionString);
		fprintf(objFile, "%s %s %c\n", AddressStr, InstructionString,
				instruction->codeSegment[i].linkInformation);
	}

	for (i = 0; i < instruction->DC; i++)
	{
		char AddressStr[5] = "0000";
		char InstructionString[9] = "00000000";
		ConvertToBase12(instruction->IC + instruction->codeBaseAddress + i, AddressStr);
		ConvertWordToBase12(&(instruction->dataSegment[i].wordEncoding), InstructionString);
		fprintf(objFile, "%s %s\n", AddressStr, InstructionString);
	}

	fclose(objFile);

	writeMemoryFile = TRUE;

	return writeMemoryFile;
}

Boolean WriteEntriesFile(char* objectFileName, SymbolTable* entries)
{
	Boolean writeEntryFile = FALSE;

	int i = 0;
	FILE* objFile = fopen(objectFileName, "w");

	if (objFile == NULL)
	{
		writeEntryFile = FALSE;
	}

	for (i = 0; i < entries->SC; i++)
	{
		char AddressStr[5] = "0000";
		ConvertToBase12(entries->symbols[i].offset, AddressStr);
		fprintf(objFile, "%s %s\n", entries->symbols[i].symbolName, AddressStr);
	}

	fclose(objFile);

	writeEntryFile = TRUE;

	return writeEntryFile;
}

Boolean WriteExternalsFile(char* objectFileName, SymbolTable* externs)
{
	Boolean writeExternFile = FALSE;

	int i = 0;
	FILE* objFile = fopen(objectFileName, "w");

	if (objFile == NULL)
	{
		writeExternFile = FALSE;
	}

	for (i = 0; i < externs->SC; i++)
	{
		char AddressStr[5] = "0000";
		ConvertToBase12(externs->symbols[i].offset, AddressStr);
		fprintf(objFile, "%s %s\n", externs->symbols[i].symbolName, AddressStr);
	}

	fclose(objFile);

	writeExternFile = TRUE;

	return writeExternFile;
}

Boolean WriteOutPutFiles(Memory* instructions,
	SymbolTable* entrySymbols,
	SymbolTable* externSymbols)
{
	Boolean writeSuccessful = FALSE;

	char* objectFileName = (char*) malloc(strlen(currProgramFileName) + 1);
	char* entryFileName  = (char*) malloc(strlen(currProgramFileName) + 1);
	char* extrenFileName = (char*) malloc(strlen(currProgramFileName) + 1);

	strcpy(objectFileName, currProgramFileName);
	strcat(objectFileName, OBJECT_FILE_SUFFIX);

	strcpy(entryFileName, currProgramFileName);
	strcat(entryFileName, ENTRY_FILE_SUFFIX);

	strcpy(extrenFileName, currProgramFileName);
	strcat(extrenFileName, EXTERN_FILE_SUFFIX);

	writeSuccessful = WriteMemoryFile(objectFileName, instructions);

	if (writeSuccessful == TRUE)
	{
		writeSuccessful = WriteEntriesFile(entryFileName, entrySymbols);

		if (writeSuccessful == TRUE)
		{
			writeSuccessful = WriteExternalsFile(extrenFileName, externSymbols);
		}
	}
	return writeSuccessful;
}

/* Create the assembly process output files of the current program
 * TRUE is returned in case all output files where successfully written, false otherwise
 * Note : the given lists are expected to be string lists */
//Boolean WriteOutPutFiles(FileLines* instructions,
//						 FileLines* entrySymbols,
//						 FileLines* externSymbols)
//{
//	Boolean writeSuccessful = FALSE;
//
//	char* objectFileName = (char*) malloc(strlen(currProgramFileName) + 1);
//	char* entryFileName  = (char*) malloc(strlen(currProgramFileName) + 1);
//	char* extrenFileName = (char*) malloc(strlen(currProgramFileName) + 1);
//
//	strcpy(objectFileName, currProgramFileName);
//	strcat(objectFileName, OBJECT_FILE_SUFFIX);
//
//	strcpy(entryFileName, currProgramFileName);
//	strcat(entryFileName, ENTRY_FILE_SUFFIX);
//
//	strcpy(extrenFileName, currProgramFileName);
//	strcat(extrenFileName, EXTERN_FILE_SUFFIX);
//
//	writeSuccessful = WriteFile(objectFileName,	instructions);
//
//	if(writeSuccessful == TRUE)
//	{
//		writeSuccessful = WriteFile(entryFileName, entrySymbols);
//
//		if(writeSuccessful == TRUE)
//		{
//			WriteFile(extrenFileName, externSymbols);
//		}
//	}
//
//	return writeSuccessful;
//}

StatementType ClasifyStatement(char* line)
{
	StatementType statementType = COMMAND;

	char* statment = TrimWhiteSpaces(line);

	if (*statment == DIRECTIVE_START_MARKER)
	{
		statementType = DIRECTIVE;
	}

	return statementType;
}

//void InitGeneralProgramLine()
//{
//	ParsedProgramLine
//	FileLine* originalFileLine;
//		Boolean shouldBeHandled;
//		CommandLineTokens* commandTokens;
//		ErrorMessage errorMessage;
//}

//void RemoveEmptyAndCommentLines(List* programLines)
//{
//	Node_ptr currProgramLine, programLineToRemove;
//	currProgramLine = programLines->head;
//
//	while (currProgramLine != List_End())
//	{
//		char* currLine = (char*) currProgramLine->data;
//
//		trimWhiteSpaces(currLine);
//
//		if (strcmp(currLine, EMPTY_LINE) == EQUAL_STRINGS ||
//			*currLine == COMMENT_LINE_MARKER)
//		{
//			programLineToRemove = currProgramLine;
//			currProgramLine = List_Next(programLineToRemove);
//			List_Delete(programLines, currProgramLine);
//		}
//	}
//}

/* Trims spaces from the beginning of the string up to a non space letter */
char* TrimWhiteSpaces(char* string)
{
	char* ch = string;

	while ( ((*ch) != LINE_END) && 		/* Check that end of line hasn't been encountered */
			//((*ch) != EMPTY_LINE) && 	/* Check that the line isn't empty */
			(IS_WHITE_SPACE(*ch) == TRUE)) /* Check that this is a white space */
	{
		ch++;
	}

	return ch;
}

/* Seeks the end of the given parameter,
 * advances the string up to a space letter, symbol end marker or end of line */
char* FindTokenEnd(char* string)
{
	char* ch = string;

	while ( ((*ch) != LINE_END) && 				/* Check that end of line hasn't been encountered */
			(IS_WHITE_SPACE(*ch) == FALSE) && 	/* Check that this isn't a white space */
			((*ch) != SYMBOL_END_MARKER) &&		/* Check that end of symbol hasn't been encountered */
			((*ch) != PARAMETER_SEPARATOR) )	/* Check that parameter separator hasn't been encountered */
	{
		ch++;
	}

	return ch;
}

Boolean IsSymbol(char* tokenStringEnd)
{
	Boolean isSymbol = FALSE;

	if ((*tokenStringEnd) == SYMBOL_END_MARKER)
	{
		isSymbol = TRUE;
	}

	return isSymbol;
}

Boolean IsDirective(char* tokenStringStart)
{
	Boolean isDirective = FALSE;

	if ((*tokenStringStart) == DIRECTIVE_START_MARKER)
	{
		isDirective = TRUE;
	}

	return isDirective;
}

Boolean GetToken(char* tokenStart, char* tokenEnd, char* destination, int expectedLength)
{
	Boolean isTokenValid = FALSE;

	int tokenLength = tokenEnd - tokenStart + 1;

	if (tokenLength <= expectedLength)
	{
		strncat(destination, tokenStart, tokenLength);

		tokenStart += tokenLength;

		isTokenValid = TRUE;
	}

	return isTokenValid;
}

void ConvertToBase12(unsigned int valToConvert, char* base12Val)
{
	unsigned int MaskedVal = valToConvert & WORD_BITMASK;

	char tempString[20];
	int NumberOfDigits = 0;
	int i = 0;
	while (MaskedVal != 0)
	{
		/*the order of the digits we calculate is reversed so first save into temp then reverse them*/
		unsigned int CurrVal = MaskedVal % BASE;
		tempString[NumberOfDigits] = Int12ToEncodingChar[CurrVal];
		MaskedVal = MaskedVal / BASE;
		NumberOfDigits++;
	};

	for (i = 0; i < NumberOfDigits; i++)
	{
		base12Val[strlen(base12Val) - 1 - i] = tempString[i];
	}
}

void ConvertWordToBase12(Word* wordToCode, char* base12Word)
{
	unsigned int val = (unsigned int)(wordToCode->DataWord);
	ConvertToBase12(val, base12Word);

}

/* Divides the given command line to tokens :
 * symbol (if exists), directive or command name (if exists) and operands (if any exists) */
//DivideTokensResult DivideCommandLineTokens(char* currLine, int lineNumber)
//{
//	/* A boolean value indicating if divide operation passed or failed */
//	Boolean errorInCommandLine = FALSE;
//
//	/* Dividing operation result containing the separated tokens
//	 * or the error message if operation failed */
//	DivideTokensResult result;
//	result.devideSucceded = FALSE;
//
//	/* Create a new initialized command tokens structure for the given line */
//	CommandLineTokens* commandTokens = InitCommandTokens();
//
//	/* Save the line number */
//	commandTokens->lineNumber = lineNumber;
//
//	/* Initialize start and end positions */
//	char* currTokenStart = TrimSpaces(currLine);
//	char* currTokenEnd = currTokenStart;
//
//	/* Check if current line needs handling */
//	if (IS_NOT_EMPTY_LINE(*currTokenStart) && IS_NOT_COMMENT_LINE(*currTokenStart))
//	{
//		/* In case it isn't an empty line or a comment line, attempt to find current token end */
//		currTokenEnd = FindTokenEnd(currTokenStart);
//
//		/* Check if this is a symbol name */
//		if (IsSymbol(currTokenEnd) == TRUE)
//		{
//			if (GetToken(currTokenStart, currTokenEnd,
//					commandTokens->symbol, MAX_SYMBOL_NAME_LENGTH) == TRUE)
//			{
//				currTokenStart++; /* skip symbol end marker */
//
//				currTokenStart = TrimSpaces(currTokenStart);
//				currTokenEnd = FindTokenEnd(currTokenStart);
//			}
//			else
//			{
//				errorInCommandLine = TRUE;
//				sprintf(result.errorMessage, "symbol name exceeds the legal size");
//			}
//		}
//		if (errorInCommandLine != TRUE)
//		{
//			/* Check if this is a directive type */
//			if (IsDirective(currTokenStart) == TRUE)
//			{
//				if (GetToken(currTokenStart, currTokenEnd,
//						commandTokens->directive, MAX_DIRECTIVE_NAME_LENGTH) == TRUE)
//				{
//					/* Get directive operands start */
//					currTokenStart = TrimSpaces(currTokenStart);
//					/* Get current directive operand end */
//					currTokenEnd = FindTokenEnd(currTokenStart);
//
//					/* Directive arguments are expected, read to end of the line */
//					while (currTokenStart != LINE_END)
//					{
//						if (GetToken(currTokenStart, currTokenEnd,
//								commandTokens->Operands, MAX_OPERANDS_COUNT) == TRUE)
//						{
//							currTokenStart = TrimSpaces(currTokenStart);
//							currTokenEnd = FindTokenEnd(currTokenStart);
//						}
//						else
//						{
//							errorInCommandLine = TRUE;
//							sprintf(result.errorMessage,
//									"arguments supplied for %s exceed the legal size",
//									commandTokens->directive);
//							break;
//						}
//					}
//				}
//				else
//				{
//					errorInCommandLine = TRUE;
//					sprintf(result.errorMessage, "directive name exceeds the legal size");
//				}
//			}
//			/* Otherwise command name is expected */
//			else
//			{
//				if (GetToken(currTokenStart, currTokenEnd,
//						commandTokens->commandName, MAX_COMMAND_NAME_LENGTH) == TRUE)
//				{
//					currTokenStart = TrimSpaces(currTokenStart);
//					currTokenEnd = FindTokenEnd(currTokenStart);
//
//					while (currTokenStart != LINE_END)
//					{
//						if (GetToken(currTokenStart, currTokenEnd,
//								commandTokens->Operands, MAX_OPERANDS_COUNT) == TRUE)
//						{
//							currTokenStart = TrimSpaces(currTokenStart);
//							currTokenEnd = FindTokenEnd(currTokenStart);
//						}
//						else
//						{
//							errorInCommandLine = TRUE;
//							sprintf(result.errorMessage,
//									"arguments supplied for %s exceed the legal size",
//									commandTokens->commandName);
//							break;
//						}
//					}
//				}
//				else
//				{
//					errorInCommandLine = TRUE;
//					sprintf(result.errorMessage, "command name exceeds the legal size");
//				}
//			}
//		}
//	}
//
//	result.commandTokens = commandTokens;
//
//	if (errorInCommandLine == FALSE)
//	{
//		result.devideSucceded = TRUE;
//	}
//
//	return result;
//}

/* ----------------------------------------------------------------------------
 *	Function Definition End
 * ----------------------------------------------------------------------------*/
